Add SSH deployment support with release management#1170
Draft
Soner (shyim) wants to merge 10 commits into
Draft
Conversation
Adds "shopware-cli project deploy" building on the existing environments abstraction: environments get a new "ssh" type with connection settings and a deployment section (path, keep_releases, shared files/dirs, hooks). Deployment methods are pluggable through a Deployer interface in internal/deployment so SFTP/PaaS backends can be added later with the same CLI surface. The first implementation deploys over SSH using a Deployer-style layout: releases are uploaded as tarball streams into releases/<timestamp>, shared files and directories are symlinked from shared/, the current symlink is switched atomically and old releases are pruned. When no pre_switch hooks are configured the Shopware Deployment Helper is run automatically when present. "project deploy rollback [release]" switches back to a previous release and marks the rolled-back-from release as bad so later rollbacks skip it, and "project deploy releases" lists the releases on the target. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Environments can now list additional servers under ssh.hosts; each entry inherits unset connection settings (port, user, authentication) from the ssh block. The primary host plus all additional hosts form the deployment target. Deploys upload the release to all hosts in parallel, run hooks host after host so database work like migrations never executes concurrently, and switch the current symlink everywhere only after every host finished its pre-switch phase, so all hosts serve the same release. A failure on any host before the switch aborts the deployment with the running release untouched everywhere. Rollback validates that the target release exists on every host before switching any of them, and "deploy releases" lists the releases grouped per host. Shared symlink creation now uses ln -sfn and best-effort seeding, making re-deploys after partial failures idempotent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Implements the Executor interface for ssh environments so commands like "project console cache:clear --env production", "project clear-cache" and "project worker" run on the deployment target with the same CLI surface as local and docker environments. The SSHExecutor wraps the system ssh client, so the user's SSH agent and configuration are honored and interactive console commands get a TTY when stdin is a terminal. Commands execute inside the currently deployed release (<deployment.path>/current) on the primary host, with environment variables inlined and all arguments shell-quoted. Because "project console" disables cobra flag parsing, shopware-cli's own flags (--env/-e, --project-config) are now extracted from the arguments before the console command name; everything after it is passed to bin/console unchanged, keeping Symfony's own --env flag usable. "project clear-cache --env <name>" runs bin/console cache:clear on the remote host for ssh environments instead of falling back to the local cache directory. The shell quoting helper moved to internal/shell so the deployment and executor packages share it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Unifies deployments and remote command execution on the system ssh client with OpenSSH connection multiplexing (ControlMaster). The deployment previously used the Go SSH library with its own connection, so it could not share anything with the executor; now both build their commands through the new internal/sshcmd package, which adds ControlMaster=auto with a shared ControlPath socket (~/.ssh/shopware-cli-%C) and ControlPersist=60. The first command opens a master connection per host and every following command — within a deploy or a "project console" right after it — reuses it, skipping the TCP, key exchange and authentication handshake (about 2.5x faster per command on loopback, more over real networks). Using the ssh client everywhere also means deployments now honor the user's ssh_config, SSH agent and ProxyJump settings. Multiplexing is on by default (except on Windows, where OpenSSH does not support it) and can be disabled per environment with ssh.control_master: false to defer to your own ssh_config. Password based authentication uses sshpass when available and falls back to the interactive prompt. The ssh.passphrase setting is removed: encrypted keys are handled by the SSH agent or an interactive prompt, which the previous in-process client could not delegate. golang.org/x/crypto is no longer a direct dependency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
The deployment section (path, releases, shared paths, hooks) describes
the SSH releases-style strategy specifically. Future environment types
like PaaS will deploy in a completely different way with their own
settings, so the section moves from the environment level into the ssh
block:
environments:
production:
type: ssh
ssh:
host: shop.example.com
deployment:
path: /var/www/shopware
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Check fmt.Fprint results in the releases listing, collapse an else-if in the rollback target validation and mark the intentional nil error in EnvironmentStatus, where a non-zero exit of test -e means no release is deployed rather than a failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
The remote branch of project clear-cache duplicated what resolveExecutor already does (read config, resolve the environment, build the executor). Branch on the resolved executor's type instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Drop the ssh type check in project clear-cache: the executor abstraction exists so callers do not dispatch on the environment type. With --env the command now runs bin/console cache:clear through whatever executor the environment resolves to (local, docker, ssh). Without --env the previous behavior (admin API or removing var/cache) is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
The completion function received --env/-e as positional input because the console command disables cobra flag parsing, so it completed against the default environment and treated the flag as the console command name. Apply the same flag extraction as the run path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
Extends the executor abstraction with DatabaseConnection, so project dump asks the environment for its database instead of assuming a locally reachable server. Local, docker and symfony-cli environments keep the existing behavior (defaults plus the project's Symfony env files); ssh environments read the deployed release's env files (.env.dist, .env, .env.local and the APP_ENV variants) for the DATABASE_URL and route the connection through the ssh client. The transport uses stdio forwarding (ssh -W) registered as a custom go-sql-driver dial function: the configured address stays the database address as seen from the server and is dialed through an ssh channel at connect time. No local port is opened, connection flags (--host, --username, ...) override the remote settings naturally, and every connection - including parallel dumps - rides the multiplexed ControlMaster connection. All dump features (anonymization, rewrites, nodata, where) keep working since the dumper still runs locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ev2G1VYZ3kkAzJ3cCeGZtZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds comprehensive SSH-based deployment capabilities to the CLI, enabling users to deploy Shopware projects to remote servers using a releases/shared directory layout with atomic symlink switching, similar to Deployer (deployer.org).
Key Changes
New Deployment Infrastructure
internal/deployment/- New package providing deployment abstraction:deployment.go- CoreDeployerinterface and types (Release,HostReleases,Options)ssh_deployer.go- SSH-based deployer implementing releases/shared/current symlink patternssh_connection.go- SSH connection wrapper using system ssh clientconnection.go- Connection interface for testabilityarchive.go- Project archiving with configurable exclusionsSSH Command Building
internal/sshcmd/- New package for SSH client command construction:sshcmd.go- Builds SSH arguments from environment config with ControlMaster multiplexing supportRemote Command Execution
internal/executor/ssh.go- New SSH executor for running commands on deployed releases:currentsymlinkConfiguration Schema
internal/shop/config.go- ExtendedEnvironmentConfigwith:EnvironmentSSH- SSH connection settings (host, port, user, authentication, known_hosts, ControlMaster control)EnvironmentDeployment- Deployment configuration (path, keep_releases, shared files/dirs, hooks)EnvironmentDeploymentHooks- Build, pre-switch, and post-switch hooksCLI Commands
cmd/project/project_deploy.go- Main deploy commandcmd/project/project_deploy_releases.go- List releases on targetcmd/project/project_deploy_rollback.go- Rollback to previous releasecmd/project/project_console.go- Enhanced to support SSH environments with flag strippingUtilities
internal/shell/quote.go- POSIX shell quoting for safe command constructioninternal/executor/factory.go- Extended to support SSH executor typeNotable Implementation Details
ln -sfn+mv -fTfor atomic current symlink updates, preventing partial deploymentsshopware-deployment-helperwhen present and no pre-switch hooks are configuredConfiguration Example